package pojo;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/***
 * @Author 徐庶
 * @Slogan 致敬大师，致敬未来的你
 */
public class Account {
    public Integer id;        // id
    public String name;
    public Integer balance = 100;   // 账户
    public AtomicInteger atomicBalance = new AtomicInteger(100);

    public void transfer(Account target, int amount) throws InterruptedException {
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + "begin");
            Thread.sleep(2000);
            synchronized (target) {     //ThreadLocal ... CAS
                this.balance -= amount;
                target.balance += amount;
                System.out.println(Thread.currentThread().getName() + "end");
            }
        }
    }




    public void transfer1(Account target, int amount) throws InterruptedException {
        synchronized (this) {
            this.atomicBalance.addAndGet(-amount) ;
            target.atomicBalance.addAndGet(amount) ;
        }
    }

    // 破坏循环等待
    public void transfer4(Account target,int amount){

        Account left = this;
        Account right = target;
        if (this.id > target.id) {
            left = target;
            right = this;
        }

        //锁定转出账户
        synchronized (left) {
            try {
                System.out.printf("当前线程%s,获得对象锁%s \n",Thread.currentThread().getName(),left.toString());
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //锁定转入账户
            synchronized (right) {
                if (this.balance<amount){
                    return;
                }
                System.out.printf("当前线程%s,获得对象锁%s \n",Thread.currentThread().getName(),right.toString());
                this.balance = balance - amount;
                target.balance = target.balance + amount;
            }
        }
    }


    // 破坏不可抢占——可以让对方分手
    private final Lock lock = new ReentrantLock();

    public void transfer2(Account target, int amount) throws InterruptedException {
        while (true) {
            //锁定转出账户  并设置超时    握不住就放手
            if (this.lock.tryLock(2, TimeUnit.SECONDS)) {
                try {
                    System.out.printf("-当前线程%s,获得对象锁%s \n", Thread.currentThread().getName(), this.toString());
                    //锁定转入账户
                    if (target.lock.tryLock()) {
                        try {
                            System.out.printf("=当前线程%s,获得对象锁%s \n", Thread.currentThread().getName(), target.toString());
                            if (this.balance < amount) {
                                return;
                            }
                            //Thread.sleep(5000);
                            this.balance = balance - amount;
                            target.balance = target.balance + amount;
                            System.out.println("转账成功！");
                            break;
                        } finally {
                            System.out.printf("=当前线程%s,释放对象锁%s \n", Thread.currentThread().getName(), target.toString());

                            target.lock.unlock();
                        }
                    }
                } finally {
                    System.out.printf("-当前线程%s,释放对象锁%s \n", Thread.currentThread().getName(), this.toString());
                    this.lock.unlock();
                }

            } else {

            }
        }
    }



    //  破坏占有且等待     占有不能破坏，那就不要等待， 我2个都占了，   等我用完2个你再用
    private Allocator actr=Allocator.getAllocator();
    public void transfer3(Account target, int amt){
        // 一次性申请转出账户和转入账户，直到成功
        while(!actr.apply(this, target)){;} //并发时只有一个会成功，如果需要都成功，可以改成while(true) 或者使用等待-通知 (wait()/notify())

        try{
            // 锁定转出账户
            synchronized(this){
                // 锁定转入账户
                synchronized(target){
                    if (this.balance > amt){
                        this.balance -= amt;
                        target.balance += amt;
                    }
                }
            }
        } finally {
            // 用完释放， 交给其他线程用
            actr.free(this, target);
        }
    }

    // 利用一个账户管理员同时占有2个资源 ，其他线程空轮询，
    static class Allocator {
        private static Allocator allocator = new Allocator();

        private Allocator() {
        }

        public static Allocator getAllocator() {
            return allocator;
        }

        private List<Object> als =
                new ArrayList<>();

        // 一次性申请所有资源
        synchronized boolean apply(
                Object from, Object to) {
            if (als.contains(from) ||
                    als.contains(to)) {
                return false;
            } else {
                als.add(from);
                als.add(to);
            }
            return true;
        }

        // 归还资源
        synchronized void free(
                Object from, Object to) {
            als.remove(from);
            als.remove(to);
        }
    }


    public Account(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Account() {
    }
}
